﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using WMM.Tapioca.Linkbase;
using Saxon.Api;
using WMM.Tapioca.Utilities;
using WMM.Tapioca.Taxonomy;
using System.Collections;
using System.Xml;
using WMM.Tapioca.Instance;
using System.Text.RegularExpressions;
using WMM.Tapioca.Formula.Filters;
using WMM.Tapioca.Formula.Functions;
using WMM.Tapioca.XbrlDimension;

namespace WMM.Tapioca.Formula
{
    /// <summary>
    /// A variable set is a set of fact and/or general variables that are able 
    /// to reference each-other using XPath variable references. 
    /// </summary>
    public class VariableSet : FormulaResource
    {
        protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        internal DTS dts;
        internal Dictionary<string, string> namespaces = new Dictionary<string, string>();

        internal string nsvariable = "http://xbrl.org/2008/formula/conformance/variable";
        XNamespace xbrldi = Constants.XBRLDI;

        // Create a Processor instance.
        internal static Processor processor = new Processor();

        // Create the XPath expression.
        internal XPathCompiler compiler = processor.NewXPathCompiler();

        internal Dictionary<string, string> parameterDictionary;
        internal Dictionary<string, List<List<FactItem>>> variableSequences;
        internal Dictionary<string, List<FactItem>> variableItems;
        internal Dictionary<string, List<FactItem>> variableAsSequenceEntries;
        internal List<List<FactItem>> variableAsItem;
        internal List<string> variableNamesAsItem;

        internal Graph dependencyGraph;

        internal Dictionary<string, bool> aspectsCoveredInFilter;

        internal string aspectIncludeDimension;

        internal string aspectExcludeDimension;

        internal List<List<FactItem>> sequencesConceptRelationFilter = new List<List<FactItem>>();

        protected int assertionsSatisfied;
        protected int assertionsNotSatisfied;

        protected string message;

        public string Message
        {
            get { return message; }
            set { message = value; }
        }

        private string aspectModel;

        /// <returns> A variable set's aspect model </returns>
        public string AspectModel
        {
            get { return aspectModel; }
            set { aspectModel = value; }
        }

        private bool implicitFiltering;

        /// <returns>A variable set uses implicit filtering if its @implicitFiltering attribute equals true
        /// and it does not use implicit filtering if the @implicitFiltering attribute equals false.</returns>
        public bool ImplicitFiltering
        {
            get { return implicitFiltering; }
            set { implicitFiltering = value; }
        }
       
        private string name;

        /// <returns> The QName of a variable. </returns>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private XElement variable;
        /// <summary>
        /// The XPath variable reference.
        /// </summary>
        public XElement Variable
        {
            get { return variable; }
            set { variable = value; }
        }

        private Precondition precondition;

        /// <summary>
        ///  Preconditions define conditions that must be satisfied for a variable set evaluation to occur. 
        /// </summary>
        public Precondition Precondition
        {
            get { return precondition; }
            set { precondition = value; }
        }

        private string from;

        /// <returns> attribute "from". </returns>
        public string From
        {
            get { return from; }
            set { from = value; }
        }
        private string to;

        /// <returns> attribute "to". </returns>
        public string To
        {
            get { return to; }
            set { to = value; }
        }

        private XElement assertion;

        /// <returns> Content of attribute "use". </returns>
        public XElement Assertion
        {
            get { return assertion; }
            set { assertion = value; }
        }

        List<Parameter> parameters;

        /// <returns> List of parameters. </returns>
        public List<Parameter> Parameters
        {
            get { return parameters; }
            set { parameters = value; }
        }

        List<FactVariable> factVariables;

        /// <returns> List of Fact Variables. </returns>
        public List<FactVariable> FactVariables
        {
            get { return factVariables; }
            set { factVariables = value; }
        }

        List<GeneralVariable> generalVariables;

        /// <returns> List of Fact Variables. </returns>
        public List<GeneralVariable> GeneralVariables
        {
            get { return generalVariables; }
            set { generalVariables = value; }
        }

        List<VariableSet> variableSetArcs;

        /// <returns> List of VariableSet. </returns>
        public List<VariableSet> VariableSetArcs
        {
            get { return variableSetArcs; }
            set { variableSetArcs = value; }
        }

        private List<Filter> groupFilters;

        public List<Filter> GroupFilters
        {
            get { return groupFilters; }
            set { groupFilters = value; }
        }

        private Resource labelAssertion;

        /// <returns> Label is associated with assertion. </returns>
        public Resource LabelAssertion
        {
            get { return labelAssertion; }
            set { labelAssertion = value; }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public VariableSet(Arc arc)
            : base(arc)
        {
            Parameters = new List<Parameter>();
            FactVariables = new List<FactVariable>();
            generalVariables = new List<GeneralVariable>();
            variableSetArcs = new List<VariableSet>();
            groupFilters = new List<Filter>();

            foreach (XAttribute attr in arc.ArcElement.Attributes())
            {
                if (attr.Name.LocalName.Equals("from"))
                {
                    from = attr.Value;
                }

                if (attr.Name.LocalName.Equals("to"))
                {
                    to = attr.Value;
                }

                if (attr.Name.LocalName.Equals("name"))
                {
                    name = attr.Value;
                }
            }

            foreach (Linkbase.Resource resource in arc.ExtendedLinkParent.Resources)
            {
                if (resource.Label.Equals(this.from))
                {
                    assertion = resource.ResourceXElement;
                }

                if (resource.Label.Equals(this.to))
                {
                    variable = resource.ResourceXElement;
                }
            }
        }

        internal VariableSet(Resource resource, Dictionary<string, string> namespaces)
            : base(resource)
        {
            this.namespaces = namespaces;    
 
            Parameters = new List<Parameter>();
            FactVariables = new List<FactVariable>();
            generalVariables = new List<GeneralVariable>();
            variableSetArcs = new List<VariableSet>();
            groupFilters = new List<Filter>();
            aspectsCoveredInFilter = new Dictionary<string, bool>();

            parameterDictionary = new Dictionary<string, string>();
            variableSequences = new Dictionary<string, List<List<FactItem>>>();
            variableItems = new Dictionary<string, List<FactItem>>();
            variableAsSequenceEntries = new Dictionary<string, List<FactItem>>();
            variableAsItem = new List<List<FactItem>>();
            variableNamesAsItem = new List<string>();
        }

        internal VariableSet() { }


        protected void AddCoveredAspect(FactVariable factVariable, Arc arc)
        {
            List<Resource> filters = GetResources(arc.ToId, arc.ExtendedLinkParent);

            bool cover = (bool)arc.ArcElement.Attribute("cover");

            if (cover)
            {
                foreach (Resource filter in filters)
                {
                    factVariable.CoveredAspects.Add(filter.LocalName);
                }
            }
        }



        protected List<Filter> GetSubFilters(string booleanFilterLabel, ExtendedLink extendedLink)
        {
            List<Filter> subFilters = new List<Filter>();

            foreach (Arc arc in extendedLink.Arcs)
            {
                if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/boolean-filter") && arc.FromId == booleanFilterLabel)
                {
                    Resource filterResource = GetResource(arc.ToId, arc.ExtendedLinkParent);
                    subFilters.Add(Filter.CreateFilter(arc, filterResource, this));
                }
            }

            return subFilters;
        }


        protected List<Resource> GetResources(string resourceLabel, ExtendedLink extendedLinkParent)
        {
            List<Resource> resources = new List<Resource>();

            foreach (Linkbase.Resource resource in extendedLinkParent.Resources)
            {
                if (resource.Label.Equals(resourceLabel))
                {
                    resources.Add(resource);
                }
            }

            return resources;
        }


        internal void SetContextItem(Processor processor, XPathSelector selector, XmlNode contextItem)
        {
            XdmNode indoc = null;

            if (dts.Instance != null)
            {
                indoc = processor.NewDocumentBuilder().Build(contextItem);
                IEnumerator e = indoc.EnumerateAxis(XdmAxis.Child);
                XdmNode n = (XdmNode)e.Current;
                selector.ContextItem = n;
            }
            else
            {
                indoc = processor.NewDocumentBuilder().Build(new XmlDocument().CreateElement("xbrl"));
                selector.ContextItem = indoc;
            }
        }


        internal List<FactItem> FilteringFacts(VariableSet variableSet, FactVariable factVariable, List<FactItem> itemsToFilter)
        {
            foreach (Filter filter in factVariable.Filters)
            {
                itemsToFilter = filter.EvaluateFilter(variableSet, itemsToFilter, dts);
            }

            return itemsToFilter;
        }


        protected List<FactItem> GetItems(List<Fact> facts)
        {
            List<FactItem> items = new List<FactItem>();

            foreach (Fact fact in facts)
            {
                if (fact is FactItem)
                {
                    items.Add(fact as FactItem);
                }

                if (fact is FactTuple)
                {
                    items.AddRange(GetItems((fact as FactTuple).Facts));
                }
            }

            return items;
        }


        internal void DeclareVariables(VariableSet variableSetType, XPathCompiler compiler)
        {
            foreach (string prefix in namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, namespaces[prefix]);
            }

            foreach (VariableSet variableSet in variableSetType.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("factVariable"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }

                if (variableSet.Variable.Name.LocalName.Equals("generalVariable"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }

                if (variableSet.Variable.Name.LocalName.Equals("parameter"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }
            }
        }


        internal void DeclareParameters(VariableSet variableSetType, XPathCompiler compiler)
        {
            foreach (string prefix in namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, namespaces[prefix]);
            }

            foreach (VariableSet variableSet in variableSetType.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("parameter"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }
            }
        }


        internal Variable GetVariable(string name, VariableSet assertionOrFormula)
        {
            foreach (VariableSet variableSet in assertionOrFormula.VariableSetArcs)
            {
                if (name.Equals(variableSet.Name))
                {
                    foreach (FactVariable factVariable in assertionOrFormula.FactVariables)
                    {
                        if (factVariable.Label.Equals(variableSet.To))
                        {
                            return factVariable;
                        }
                    }

                    foreach (GeneralVariable generalVariable in assertionOrFormula.GeneralVariables)
                    {
                        if (generalVariable.Label.Equals(variableSet.To))
                        {
                            return generalVariable;
                        }
                    }
                }
            }

            return null;
        }


        internal FactVariable GetFactVariable(string name, VariableSet assertionOrFormula)
        {
            foreach (VariableSet VariableSet in assertionOrFormula.VariableSetArcs)
            {
                if (name.Equals(VariableSet.Name))
                {
                    foreach (FactVariable factVariable in assertionOrFormula.FactVariables)
                    {
                        if (factVariable.Label.Equals(VariableSet.To))
                        {
                            return factVariable;
                        }
                    }
                }
            }

            return null;
        }


        internal GeneralVariable GetGeneralVariable(VariableSet variableSet, VariableSet variableSetArc)
        {
            foreach (GeneralVariable generalVariable in variableSet.GeneralVariables)
            {
                if (generalVariable.Label.Equals(variableSetArc.To))
                {
                    return generalVariable;
                }
            }

            return null;
        }


        internal void BindParameters(XPathSelector selector, VariableSet variableSet, Dictionary<string, string> parameterDictionary)
        {
            double value;

            foreach (VariableSet variable in variableSet.VariableSetArcs)
            {
                foreach (Parameter parameter in this.Parameters)
                {
                    if (variable.To.Equals(parameter.Label))
                    {
                        string prefix = variable.Name.GetPrefix();

                        if (prefix.Length > 0)
                        {
                            if (double.TryParse(parameter.Value, out value))
                            {
                                selector.SetVariable(new QName(variableSet.namespaces[prefix], variable.Name), new XdmAtomicValue(value));
                            }
                            else
                            {
                                selector.SetVariable(new QName(variableSet.namespaces[prefix], variable.Name), new XdmAtomicValue(parameter.Value));
                            }
                        }
                        else
                        {
                            if (double.TryParse(parameter.Value, out value))
                            {
                                selector.SetVariable(new QName(variable.Name), new XdmAtomicValue(value));
                            }
                            else
                            {
                                selector.SetVariable(new QName(variable.Name), new XdmAtomicValue(parameter.Value));
                            }
                        }

                        log.Debug("Formula trace: Parameter " + variable.Name + " bound value " + parameter.Value);

                        if (parameterDictionary != null)
                        {
                            parameterDictionary.Add(variable.Name, parameter.Value);
                        }
                    }
                }
            }
        }


        internal string CompileMessage(Message assertionMessage, XPathCompiler compiler, Dictionary<string, FactItem> variableEntries, Dictionary<string, string> parameterDictionary, Dictionary<string, XdmAtomicValue> variableFallBack)
        {
            RegisterExtensionFunctions();

            string message = assertionMessage.Content;

            Match match = Regex.Match(message, @"\{[^}]*\}"); // (?<=\{).*?(?=\})

            while (match.Success)
            {
                DeclareVariables(this, compiler);

                string xPathExpression = match.Value.Replace("{", "").Replace("}", "");

                XPathSelector selector = compiler.Compile(xPathExpression).Load();

                BindVariableValue(variableEntries, parameterDictionary, selector, variableAsSequenceEntries);

                BindVariableFallback(selector, variableFallBack);

                SetContextItem(processor, selector, dts.Instance.ThisXDocument.Root.GetXmlNode());

                message = message.Replace(match.Value, selector.EvaluateSingle().ToString());

                match = match.NextMatch();
            }

            return message;
        }


        internal void BindVariableValue(Dictionary<string, FactItem> variableEntries, Dictionary<string, string> parameterDictionary, XPathSelector selector, Dictionary<string, List<FactItem>> variableAsSequenceEntries)
        {
            foreach (string variableName in variableEntries.Keys)
            {
                string prefix = variableName.GetPrefix();

                XdmNode xdmNode = processor.NewDocumentBuilder().Build(variableEntries[variableName].FactElement.ToXmlNode());

                IEnumerator enumerator = xdmNode.EnumerateAxis(XdmAxis.Child);

                XdmNode elementNode = (XdmNode)enumerator.Current;

                if (prefix.Length > 0)
                {
                    selector.SetVariable(new QName(namespaces[prefix], variableName), elementNode);
                }
                else
                {
                    selector.SetVariable(new QName(variableName), elementNode);
                }
            }

            foreach (string parameterName in parameterDictionary.Keys)
            {
                string prefix = parameterName.GetPrefix();

                if (prefix.Length > 0)
                {
                    selector.SetVariable(new QName(namespaces[prefix], parameterName), new XdmAtomicValue(parameterDictionary[parameterName]));
                }
                else
                {
                    selector.SetVariable(new QName(parameterName), new XdmAtomicValue(parameterDictionary[parameterName]));
                }
            }

            BindVariableSequence(processor, selector, variableAsSequenceEntries);
        }


        protected void BindVariableSequence(Processor processor, XPathSelector selector, Dictionary<string, List<FactItem>> variableAsSequenceEntries)
        {
            foreach (string sequenceName in variableAsSequenceEntries.Keys)
            {
                List<FactItem> currentSequence = variableAsSequenceEntries[sequenceName];
                
                string prefix = sequenceName.GetPrefix();  // Item1 = name of variable

                ArrayList list = new ArrayList();

                foreach (FactItem item in currentSequence)
                {
                    XdmNode itemXdm = processor.NewDocumentBuilder().Build(item.FactElement.ToXmlNode());
                    list.Add(itemXdm);
                }

                if (prefix.Length > 0)
                {
                    selector.SetVariable(new QName(namespaces[prefix], sequenceName), new XdmValue(list));
                }
                else
                {
                    selector.SetVariable(new QName(sequenceName), new XdmValue(list));
                }

                log.Info("Formula trace: Fact Variable " + sequenceName + ": bound as sequence");
                
            }        
        }


        protected void BindVariableFallback(XPathSelector selector, Dictionary<string, XdmAtomicValue> variableFallBack)
        {
            foreach (string variableName in variableFallBack.Keys)
            {
                string prefix = variableName.GetPrefix();

                if (prefix.Length > 0)
                {
                    selector.SetVariable(new QName(namespaces[prefix], variableName), variableFallBack[variableName]);
                }
                else
                {
                    selector.SetVariable(new QName(variableName), variableFallBack[variableName]);
                }

                log.Info("Formula trace: Fact Variable " + variableName + ": bound fallback value: " + variableFallBack[variableName]);
            }
        }


        /// <summary>
        /// For every pair of variables of a formula / assertion, the value of each aspect that is not explicitly filtered* by a variable filter must be the same. 
        /// </summary>
        internal bool  ImplicitlyMatchItems(List<FactItem> cartesianProductItem, List<string> variableNamesAsItem, Dictionary<string, List<List<FactItem>>> variableSequences, VariableSet assertionOrFormula)
        {
            bool isAllAspectsCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("all", out isAllAspectsCovered))
            {
                if (isAllAspectsCovered)
                {                   
                    return true;
                }
            }

            if (cartesianProductItem.Count > 1)
            {
                for (int i = 0; i < cartesianProductItem.Count - 1; i++)
                {
                    FactVariable factVariable1 = GetFactVariable(variableNamesAsItem[i], assertionOrFormula);
                    FactVariable factVariable2 = GetFactVariable(variableNamesAsItem[i + 1], assertionOrFormula);

                    if (this.aspectsCoveredInFilter.TryGetValue("all", out isAllAspectsCovered))
                    {
                        return true;
                    }

                    if (!MatchUncoveredAspect(cartesianProductItem[i], cartesianProductItem[i + 1], assertionOrFormula, factVariable1, factVariable2, cartesianProductItem))
                    {
                        return false;
                    }
                }
            }


            if (variableSequences.Count > 0 && variableNamesAsItem.Count > 0)
            {
                int k = 0;

                foreach (string sequenceName in variableSequences.Keys)
                {
                    foreach (List<FactItem> currentSequence in variableSequences[sequenceName])
                    {
                        List<FactItem> temp = new List<FactItem>(currentSequence);

                        FactVariable factVariable1 = GetFactVariable(variableNamesAsItem[0], assertionOrFormula);
                        FactVariable factVariable2 = GetFactVariable(sequenceName, assertionOrFormula);

                        if (sequencesConceptRelationFilter.Count == 0)
                        {
                            if (!MatchUncoveredAspect(cartesianProductItem[0], currentSequence[0], assertionOrFormula, factVariable1, factVariable2, cartesianProductItem))
                            {
                                continue;
                            }
                            else
                            {
                                variableAsSequenceEntries.Add(sequenceName, currentSequence);
                                break;
                            }
                        }
                        else
                        {
                            ConceptRelationFilter conceptRelation = factVariable2.Filters[0] as ConceptRelationFilter;

                            if (IsConceptRelationshipSatisfied(cartesianProductItem[0], currentSequence, conceptRelation))
                            {
                                variableAsSequenceEntries.Add(sequenceName, currentSequence);
                                break;
                            }
                        }

                        k = k + 1;
                    }
                }
            }

            if (variableSequences.Count != variableAsSequenceEntries.Count)
            {
                return false;
            }

            if (cartesianProductItem.Count == 0 && variableSequences == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }


        private bool IsConceptRelationshipSatisfied(FactItem sourceElement, List<FactItem> currentSequence, ConceptRelationFilter conceptRelation)
        {
            string result = "true";

            bool isSourceConceptVerified = false;

            List<Concept> conceptsRelationship = new List<Concept>();

            List<Concept> conceptsSequence = new List<Concept>();

            foreach (FactItem item in currentSequence)
            {
                conceptsSequence.Add(item.Concept);
            }

            HashSet<Arc> networkRelationship = conceptRelation.GetNetworkRelationship(dts);

            foreach (Arc arc in networkRelationship)
            {
                Locator from = GetLocator(arc, arc.FromId);
                Concept conceptFrom = dts.GetConceptByID(from.HrefResourceId);

                Locator to = GetLocator(arc, arc.ToId);
                Concept conceptTo = dts.GetConceptByID(to.HrefResourceId);

                if (!string.IsNullOrEmpty(conceptRelation.Test))
                {
                    result = conceptRelation.EvaluateXPathExpression(conceptRelation.Test, arc.ArcElement.ToXmlNode());
                }

                isSourceConceptVerified = conceptRelation.ApplyAxis(sourceElement.Concept.Name, conceptsRelationship, from, conceptFrom, conceptTo);

                // xfi:root 

                //VerifyXfiRoot(sourceConcept, conceptsRelationship, from, conceptFrom);

                if (isSourceConceptVerified)
                {
                    if (conceptsRelationship.Matches(conceptsSequence))
                    {
                        return true;
                    }                    
                }
            }

            return false;
        }


        internal bool MatchUncoveredAspect(FactItem item1, FactItem item2, VariableSet assertionOrFormula, FactVariable factVariable1, FactVariable factVariable2, List<FactItem> cartesianProductItem)
        {
            // Location

            if (!MatchLocation(item1, item2, factVariable1, factVariable2))
            {
                return false;
            }

            log.Debug("Formula trace: Fact " + item2.Name + " implicit filter location passes 1 facts");

            // Concept

            if (!MatchConcept(item1, item2, factVariable1, factVariable2))
            {
                return false;
            }

            log.Debug("Formula trace: Fact " + item2.Name + " implicit filter concept passes 1 facts");

            // Unit aspect: GeneralMeasuresFilter and singleMeasureFilter

            if (!MatchUnit(item1, item2, factVariable1, factVariable2))
            {
                return false;
            }

            log.Debug("Formula trace: Fact " + item2.Name + " implicit filter unit passes 1 facts");

            // IdentifierFilter
            if (!MatchIdentifier(item1, item2, factVariable1, factVariable2))
            {
                return false;
            }

            log.Debug("Formula trace: Fact " + item2.Name + " implicit filter entity identifier passes 1 facts");

            // Period Filter

            if (!MatchPeriod(item1, item2, factVariable1, factVariable2, cartesianProductItem))
            {
                return false;
            }

            log.Debug("Formula trace: Fact " + item2.Name + " implicit filter period passes 1 facts");

            if (assertionOrFormula.AspectModel.Equals("non-dimensional"))
            {
                // Complete scenario

                if (!MatchCompleteScenario(item1, item2))
                {
                    return false;
                }

                log.Debug("Formula trace: Fact " + item2.Name + " implicit filter scenario passes 1 facts");

                // Complete segment

                if (!MatchCompleteSegment(item1, item2))
                {
                    return false;
                }

                log.Debug("Formula trace: Fact " + item2.Name + " implicit filter segment passes 1 facts");
            }


            // Dimensional

            if (assertionOrFormula.AspectModel.Equals("dimensional"))
            {
                // Non-XDT segment 

                if (!MatchNonXDTSegment(item1, item2))
                {
                    return false;
                }

                // Non-XDT scenario

                if (!MatchNonXDTScenario(item1, item2))
                {
                    return false;
                }

                // Dimension

                if (!MatchDimension(item1, item2, factVariable1, factVariable2))
                {
                    return false;
                }

                log.Debug("Formula trace: Fact " + item2.Name + " implicit filter dimension passes 1 facts");
            }

            return true;
        }


        private bool HasDimensionValue(string dimensionValue, List<XElement> explicitMembers, string dimensionQN)
        {
            string explicitMemberValue = "";

            if (explicitMembers.Count == 0)
            {
                explicitMemberValue = GetDimensionValueDefault(dts, dimensionQN);

                if (explicitMemberValue == dimensionValue)
                {
                    return true;
                }
            }

            foreach (XElement explicitMember in explicitMembers)
            {
                if (explicitMember.Value.GetLocalName() == dimensionValue)
                {
                    return true;
                }
            }

            return false;
        }


        private string GetDimensionValueDefault(DTS dts, string dimensionQN)
        {
            string defaultDimensionValue = "";

            foreach (Dimension dimension in dts.DtsDimension.Dimensions)
            {
                if (dimension.GetName().LocalName == dimensionQN.GetLocalName())
                {
                    defaultDimensionValue = dimension.DefaultConcept.Name;
                }
            }

            return defaultDimensionValue;
        }


        private bool MatchLocation(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("location", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (!itemToCompare1.Concept.Id.Equals(itemToCompare2.Concept.Id))
                    {
                        return false;
                    }
                }
            }
            else
            {
                if (!IsAspectCovered(factVariable1, "parentFilter") && !IsAspectCovered(factVariable2, "ancestorFilter") &&
                    !IsAspectCovered(factVariable1, "siblingFilter") && !IsAspectCovered(factVariable2, "locationFilter"))
                {
                    if (itemToCompare1.FactElement.Parent != itemToCompare2.FactElement.Parent)
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchConcept(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("concept", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (!itemToCompare1.Concept.Id.Equals(itemToCompare2.Concept.Id))
                    {
                        return false;
                    }
                }
            }
            else
            {
                if (!IsAspectCovered(factVariable1, "conceptName") && !IsAspectCovered(factVariable2, "conceptName"))
                {
                    if (!itemToCompare1.Concept.Id.Equals(itemToCompare2.Concept.Id))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        private bool MatchUnit(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2)
        {
             bool isAspectCovered;

             if (this.aspectsCoveredInFilter.TryGetValue("unit", out isAspectCovered))
             {
                 if (!isAspectCovered)
                 {
                     if (!itemToCompare1.UnitEquals(itemToCompare2))
                     {
                         return false;
                     }
                 }
             }
             else
             {
                 if (!IsAspectCovered(factVariable1, "generalMeasures") && !IsAspectCovered(factVariable1, "singleMeasure") &&
                     !IsAspectCovered(factVariable2, "generalMeasures") && !IsAspectCovered(factVariable2, "singleMeasure"))
                 {
                     if (itemToCompare1.Concept.SchemaElement.ElementSchemaType.IsNumeric())
                     {
                         if (!itemToCompare1.UnitEquals(itemToCompare2))
                         {
                             return false;
                         }
                     }
                 }
             }

            return true;
        }


        private bool MatchIdentifier(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("entity-identifier", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (!itemToCompare1.ContextRef.Identifier.Equals(itemToCompare2.ContextRef.Identifier))
                    {
                        return false;
                    }
                }
            }
            else
            {
                if (!IsAspectCovered(factVariable1, "identifier") &&
                    !IsAspectCovered(factVariable2, "identifier"))
                {
                    if (!itemToCompare1.ContextRef.Identifier.Equals(itemToCompare2.ContextRef.Identifier))
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchPeriod(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2, List<FactItem> cartesianProductItem)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("period", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (!XNode.DeepEquals(itemToCompare1.ContextRef.PeriodElement, itemToCompare2.ContextRef.PeriodElement))
                    {
                        return false;
                    }
                }
            }
            else
            {
                if (!IsAspectCovered(factVariable1, "period") && !IsAspectCovered(factVariable2, "period") &&
                    !IsAspectCovered(factVariable1, "instantDuration") && !IsAspectCovered(factVariable2, "instantDuration"))
                {
                    if (!XNode.DeepEquals(itemToCompare1.ContextRef.PeriodElement, itemToCompare2.ContextRef.PeriodElement))
                    {
                        return false;
                    }
                }

                if (IsAspectCovered(factVariable1, "instantDuration") && IsAspectCovered(factVariable2, "instantDuration"))
                {
                    InstantDurationFilter idf = GetFilter(factVariable1, "instantDuration") as InstantDurationFilter;

                    //FactItem durationItem = this.variableItems[idf.Variable].ElementAt(0);
                    FactItem durationItem = cartesianProductItem.ElementAt(this.variableNamesAsItem.IndexOf(idf.Variable));

                    if (Math.Abs(durationItem.ContextRef.PeriodEndDate.Year - itemToCompare1.ContextRef.InstantDate.Year) > 1 ||
                        Math.Abs(durationItem.ContextRef.PeriodEndDate.Year - itemToCompare2.ContextRef.InstantDate.Year) > 1 ||
                        Math.Abs(itemToCompare1.ContextRef.InstantDate.Year - itemToCompare2.ContextRef.InstantDate.Year) != 1)
                    {
                        return false;
                    }
                }

                if (!IsAspectCovered(factVariable1, "instantDuration") && IsAspectCovered(factVariable2, "instantDuration"))
                {
                    InstantDurationFilter idf = GetFilter(factVariable2, "instantDuration") as InstantDurationFilter;

                    if (idf.Boundary == "start")
                    {
                        if (itemToCompare1.ContextRef.IsDurationPeriod && itemToCompare1.ContextRef.PeriodStartDate.AddDays(-1) != itemToCompare2.ContextRef.InstantDate)
                        {
                            return false;
                        }
                    }
                    else if (idf.Boundary == "end")
                    {
                        if (itemToCompare1.ContextRef.IsDurationPeriod && itemToCompare1.ContextRef.PeriodEndDate != itemToCompare2.ContextRef.InstantDate)
                        {
                            return false;
                        }
                    }
                }

                if (IsAspectCovered(factVariable1, "instantDuration") && !IsAspectCovered(factVariable2, "instantDuration") && itemToCompare2.ContextRef.IsDurationPeriod)
                {
                    InstantDurationFilter idf = GetFilter(factVariable1, "instantDuration") as InstantDurationFilter;

                    if (idf.Boundary == "end")
                    {
                        if (itemToCompare1.ContextRef.InstantDate != itemToCompare2.ContextRef.PeriodEndDate)
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }


        private Filter GetFilter(FactVariable factVariable2, string filterName)
        {
            Filter filterResource = null;

            foreach (Filter filter in factVariable2.Filters)
            {
                if (filter.ResourceName == filterName)
                {
                    filterResource = filter;
                }
            }

            return filterResource;
        }


        private bool MatchCompleteScenario(FactItem itemToCompare1, FactItem itemToCompare2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("complete-scenario", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (itemToCompare1.ContextRef.ScenarioElement != null && itemToCompare2.ContextRef.ScenarioElement != null)
                    {
                        if (!XNode.DeepEquals(itemToCompare1.ContextRef.ScenarioElement, itemToCompare2.ContextRef.ScenarioElement))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                if (itemToCompare1.ContextRef.ScenarioElement != null && itemToCompare2.ContextRef.ScenarioElement == null ||
                    itemToCompare1.ContextRef.ScenarioElement == null && itemToCompare2.ContextRef.ScenarioElement != null)
                {
                    return false;
                }

                if (itemToCompare1.ContextRef.ScenarioElement != null && itemToCompare2.ContextRef.ScenarioElement != null)
                {
                    if (!XNode.DeepEquals(itemToCompare1.ContextRef.ScenarioElement, itemToCompare2.ContextRef.ScenarioElement))
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchCompleteSegment(FactItem itemToCompare1, FactItem itemToCompare2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("complete-segment", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (itemToCompare1.ContextRef.SegmentElement != null && itemToCompare2.ContextRef.SegmentElement != null)
                    {
                        if (!XNode.DeepEquals(itemToCompare1.ContextRef.SegmentElement, itemToCompare2.ContextRef.SegmentElement))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                if (itemToCompare1.ContextRef.SegmentElement != null && itemToCompare2.ContextRef.SegmentElement == null ||
                    itemToCompare1.ContextRef.SegmentElement == null && itemToCompare2.ContextRef.SegmentElement != null)
                {
                    return false;
                }

                if (itemToCompare1.ContextRef.SegmentElement != null && itemToCompare2.ContextRef.SegmentElement != null)
                {
                    if (!XNode.DeepEquals(itemToCompare1.ContextRef.SegmentElement, itemToCompare2.ContextRef.SegmentElement))
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchNonXDTSegment(FactItem itemToCompare1, FactItem itemToCompare2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("non-XDT-segment", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (itemToCompare1.ContextRef.SegmentElement != null && itemToCompare2.ContextRef.SegmentElement != null)
                    {
                        XElement segment = new XElement(itemToCompare1.ContextRef.SegmentElement);
                        segment.Descendants(xbrldi + "explicitMember").Remove();
                        segment.Descendants(xbrldi + "typedMember").Remove();

                        XElement segmentToCompare = new XElement(itemToCompare2.ContextRef.SegmentElement);
                        segmentToCompare.Descendants(xbrldi + "explicitMember").Remove();
                        segmentToCompare.Descendants(xbrldi + "typedMember").Remove();

                        segment.Value = segment.Value.Trim();
                        segmentToCompare.Value = segmentToCompare.Value.Trim();

                        if (!XNode.DeepEquals(segment, segmentToCompare))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                if (itemToCompare1.ContextRef.SegmentElement != null && itemToCompare2.ContextRef.SegmentElement != null)
                {
                    XElement segment = new XElement(itemToCompare1.ContextRef.SegmentElement);
                    segment.Descendants(xbrldi + "explicitMember").Remove();
                    segment.Descendants(xbrldi + "typedMember").Remove();

                    XElement segmentToCompare = new XElement(itemToCompare2.ContextRef.SegmentElement);
                    segmentToCompare.Descendants(xbrldi + "explicitMember").Remove();
                    segmentToCompare.Descendants(xbrldi + "typedMember").Remove();

                    segment.Value = segment.Value.Trim();
                    segmentToCompare.Value = segmentToCompare.Value.Trim();

                    if (!XNode.DeepEquals(segment, segmentToCompare))
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchNonXDTScenario(FactItem itemToCompare1, FactItem itemToCompare2)
        {
            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("non-XDT-scenario", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (itemToCompare1.ContextRef.ScenarioElement != null && itemToCompare2.ContextRef.ScenarioElement != null)
                    {
                        XElement scenario = new XElement(itemToCompare1.ContextRef.ScenarioElement);
                        scenario.Descendants(xbrldi + "explicitMember").Remove();
                        scenario.Descendants(xbrldi + "typedMember").Remove();

                        XElement scenarioToCompare = new XElement(itemToCompare2.ContextRef.ScenarioElement);
                        scenarioToCompare.Descendants(xbrldi + "explicitMember").Remove();
                        scenarioToCompare.Descendants(xbrldi + "typedMember").Remove();

                        scenario.Value = scenario.Value.Trim();
                        scenarioToCompare.Value = scenarioToCompare.Value.Trim();

                        if (!XNode.DeepEquals(scenario, scenarioToCompare))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                if (itemToCompare1.ContextRef.ScenarioElement != null && itemToCompare2.ContextRef.ScenarioElement != null)
                {
                    XElement scenario = new XElement(itemToCompare1.ContextRef.ScenarioElement);
                    scenario.Descendants(xbrldi + "explicitMember").Remove();
                    scenario.Descendants(xbrldi + "typedMember").Remove();

                    XElement scenarioToCompare = new XElement(itemToCompare2.ContextRef.ScenarioElement);
                    scenarioToCompare.Descendants(xbrldi + "explicitMember").Remove();
                    scenarioToCompare.Descendants(xbrldi + "typedMember").Remove();

                    scenario.Value = scenario.Value.Trim();
                    scenarioToCompare.Value = scenarioToCompare.Value.Trim();

                    if (!XNode.DeepEquals(scenario, scenarioToCompare))
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool MatchDimension(FactItem itemToCompare1, FactItem itemToCompare2, FactVariable factVariable1, FactVariable factVariable2)
        {
            Context context1 = itemToCompare1.ContextRef;
            Context context2 = itemToCompare2.ContextRef;

            List<XElement> explicitMembers1 = context1.ExplicitMembers;
            List<XElement> explicitMembers2 = context2.ExplicitMembers;

            bool isAspectCovered;

            if (this.aspectsCoveredInFilter.TryGetValue("dimensions", out isAspectCovered))
            {
                if (!isAspectCovered)
                {
                    if (!CompareExplicitDimensionAspect(factVariable2, explicitMembers1, explicitMembers2))
                    {
                        return false;
                    }
                }
            }
            else if (this.aspectIncludeDimension != null)
            {

            }
            else if (this.aspectExcludeDimension != null)
            {

            }
            else
            {
                if (!IsAspectCovered(factVariable1, "explicitDimension") && IsAspectCovered(factVariable2, "explicitDimension") ||
                    IsAspectCovered(factVariable1, "explicitDimension") && !IsAspectCovered(factVariable2, "explicitDimension"))
                {
                    if (!CompareExplicitDimensionAspect(factVariable2, explicitMembers1, explicitMembers2))
                    {
                        return false;
                    }
                }

                if (!IsAspectCovered(factVariable1, "explicitDimension") && !IsAspectCovered(factVariable2, "explicitDimension"))
                {
                    if (context1.ExplicitMembers.Count == context2.ExplicitMembers.Count)
                    {
                        if (!itemToCompare1.ContextRef.ExplicitMembers.Matches(itemToCompare2.ContextRef.ExplicitMembers))
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }

                if (!IsAspectCovered(factVariable1, "typedDimension") && !IsAspectCovered(factVariable2, "typedDimension"))
                {
                    if (context1.TypedMembers.Count == context2.TypedMembers.Count)
                    {
                        if (!itemToCompare1.ContextRef.TypedMembers.Matches(itemToCompare2.ContextRef.TypedMembers))
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
            }

            return true;
        }


        private bool CompareExplicitDimensionAspect(FactVariable factVariable2, List<XElement> explicitMembers1, List<XElement> explicitMembers2)
        {
            ExplicitDimensionFilter df = null;

            foreach (Filter filter in factVariable2.Filters)
            {
                if (filter.ResourceName == "explicitDimension")
                {
                    df = (ExplicitDimensionFilter)filter;

                    foreach (MemberModel memberModel in df.MembersModel)
                    {
                        HashSet<Arc> networkRelationship = null;

                        foreach (Tuple<string, string, string, string> tuple in dts.NetworkRelationships.Keys)
                        {
                            if (tuple.Item2.Equals(memberModel.Linkrole) && tuple.Item4.Equals(memberModel.Arcrole))
                            {
                                networkRelationship = dts.NetworkRelationships[tuple];
                            }
                        }

                        foreach (Arc arc in networkRelationship)
                        {
                            Locator from = GetLocator(arc, arc.FromId);
                            Concept conceptFrom = dts.GetConceptByID(from.HrefResourceId);

                            Locator to = GetLocator(arc, arc.ToId);
                            Concept conceptTo = dts.GetConceptByID(to.HrefResourceId);

                            if (memberModel.Axis == "child")
                            {
                                foreach (XElement explicitMember2 in explicitMembers2)
                                {
                                    string dimensionQN1 = (string)explicitMember2.Attribute("dimension") ?? string.Empty;

                                    if (dimensionQN1.Equals(df.Dimension))
                                    {
                                        if (conceptTo.Name == explicitMember2.Value.GetLocalName())
                                        {
                                            if (!HasDimensionValue(conceptFrom.Name, explicitMembers1, dimensionQN1))
                                            {
                                                return false;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            foreach (XElement explicitMember1 in explicitMembers1)
            {
                string dimensionQN1 = (string)explicitMember1.Attribute("dimension") ?? string.Empty;

                if (!dimensionQN1.Equals(df.Dimension))
                {
                    foreach (XElement explicitMember2 in explicitMembers2)
                    {
                        string dimensionQN2 = (string)explicitMember2.Attribute("dimension") ?? string.Empty;

                        if (dimensionQN1 == dimensionQN2 && !dimensionQN2.Equals(df.Dimension))
                        {
                            if (!XNode.DeepEquals(explicitMember1, explicitMember2))
                            {
                                return false;
                            }
                        }
                    }
                }
            }

            return true;
        }


        protected string EvaluateExpression(List<string> dependecyVariables, VariableSet variableSet, string expression)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();

            DeclareVariables2(dependecyVariables, variableSet, compiler);

            XPathSelector selector = compiler.Compile(expression).Load();

            SetContextItem(processor, selector, variableSet.dts.Instance.ThisXDocument.Root.GetXmlNode());

            variableSet.BindParameters(selector, variableSet, null);

            //BindGeneralVariable(variableSet, compiler, selector);

            foreach (string variableName in dependecyVariables)
            {
                List<FactItem> factItems = null;

                if (variableSet.variableItems.Keys.Contains(variableName))
                {
                    factItems = variableSet.variableItems[variableName];

                    foreach (FactItem item in factItems)
                    {
                        BindVariableValue(processor, variableSet, selector, variableName, item);

                        continue;
                    }
                }

                if (variableSet.variableSequences.Keys.Contains(variableName))
                {
                    List<List<FactItem>> sequences = null;

                    sequences = variableSet.variableSequences[variableName];

                    foreach (List<FactItem> sequence in sequences)
                    {
                        Dictionary<string, List<FactItem>> dict = new Dictionary<string, List<FactItem>>();
                        dict.Add(variableName, sequence);

                        BindVariableSequence(processor, selector, dict);

                        continue;
                    }
                }
            }

            string result = selector.Evaluate().ToString();

            return result;
        }


        protected static void RegisterExtensionFunctions(Processor processor)
        {
            processor.RegisterExtensionFunction(new PrecisionXfi());
            // ===================================== Contexts and units ==================================================
            processor.RegisterExtensionFunction(new ContextXfi());
            processor.RegisterExtensionFunction(new UnitXfi());
            processor.RegisterExtensionFunction(new UnitNumeratorXfi());
            processor.RegisterExtensionFunction(new UnitDenominatorXfi());
            processor.RegisterExtensionFunction(new MeasureNameXfi());
            processor.RegisterExtensionFunction(new PeriodXfi());
            processor.RegisterExtensionFunction(new ContextPeriodXfi());
            processor.RegisterExtensionFunction(new IsStartEndPeriodXfi());
            processor.RegisterExtensionFunction(new IsForeverPeriodXfi());
            processor.RegisterExtensionFunction(new IsDurationPeriodXfi());
            processor.RegisterExtensionFunction(new IsInstantPeriodXfi());
            processor.RegisterExtensionFunction(new PeriodStartXfi());
            processor.RegisterExtensionFunction(new PeriodEndXfi());
            processor.RegisterExtensionFunction(new PeriodInstantXfi());
            processor.RegisterExtensionFunction(new EntityXfi());
            processor.RegisterExtensionFunction(new ContextEntityXfi());
            processor.RegisterExtensionFunction(new IdentifierXfi());
            processor.RegisterExtensionFunction(new ContextIdentifierXfi());
            processor.RegisterExtensionFunction(new EntityIdentifierXfi());
            processor.RegisterExtensionFunction(new IdentifierValueXfi());
            processor.RegisterExtensionFunction(new IdentifierSchemeXfi());
            processor.RegisterExtensionFunction(new SegmentXfi());
            processor.RegisterExtensionFunction(new EntitySegmentXfi());
            processor.RegisterExtensionFunction(new ContextSegmentXfi());
            processor.RegisterExtensionFunction(new ScenarioXfi());
            processor.RegisterExtensionFunction(new ContextScenarioXfi());
            // =============================== Facts and tuples ===========================================
            processor.RegisterExtensionFunction(new ItemsInInstanceXfi());
            processor.RegisterExtensionFunction(new TuplesInInstanceXfi());
            processor.RegisterExtensionFunction(new ItemsInTupleXfi());
            processor.RegisterExtensionFunction(new TuplesInTupleXfi());
            // ======================================== Predicates ==================================================
            processor.RegisterExtensionFunction(new IdenticalNodesXfi());
            processor.RegisterExtensionFunction(new SEqualXfi());
            processor.RegisterExtensionFunction(new UEqualXfi());

            // ======================================== Other Functions ==================================================
            processor.RegisterExtensionFunction(new FactExplicitDimensionValueXfi());
            processor.RegisterExtensionFunction(new FactDimensionSequal2Xfi());
        }


        protected void DeclareVariables2(List<string> dependecyVariables, VariableSet variableSet, XPathCompiler compiler)
        {
            foreach (string prefix in variableSet.namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, variableSet.namespaces[prefix]);
            }

            foreach (string variableName in dependecyVariables)
            {
                string prefix = variableName.GetPrefix();

                if (prefix.Length > 0)
                {
                    compiler.DeclareVariable(new QName(variableSet.namespaces[prefix], variableName));
                }
                else
                {
                    compiler.DeclareVariable(new QName(variableName));
                }
            }
        }


        protected void BindVariableValue(Processor processor, VariableSet variableSet, XPathSelector selector, string variableName, FactItem item)
        {
            string prefix = variableName.GetPrefix();

            XdmNode xdmNode = processor.NewDocumentBuilder().Build(item.FactElement.ToXmlNode());

            IEnumerator enumerator = xdmNode.EnumerateAxis(XdmAxis.Child);

            XdmNode elementNode = (XdmNode)enumerator.Current;

            if (prefix.Length > 0)
            {
                selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), elementNode);
            }
            else
            {
                selector.SetVariable(new QName(variableName), elementNode);
            }

            //double value;

            //if (prefix.Length > 0)
            //{
            //    if (double.TryParse(item.Value, out value))
            //    {
            //        selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), new XdmAtomicValue(value));
            //    }
            //    else
            //    {
            //        selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), new XdmAtomicValue(item.Value));
            //    }
            //}
            //else
            //{
            //    if (double.TryParse(item.Value, out value))
            //    {
            //        selector.SetVariable(new QName(variableName), new XdmAtomicValue(value));
            //    }
            //    else
            //    {
            //        selector.SetVariable(new QName(variableName), new XdmAtomicValue(item.Value));
            //    }
            //}
        }


        protected List<string> GetDependecyVariables(VariableSet variableSet, string thisVariableName)
        {
            Vertex thisVertex = GetVertexThisVariable(variableSet, thisVariableName);

            List<string> dependecyVariables = new List<string>();

            if (thisVertex != null)
            {
                foreach (Edge edge in thisVertex.outEdges)
                {
                    dependecyVariables.Add(edge.destination.name);
                }
            }

            return dependecyVariables;
        }


        protected Vertex GetVertexThisVariable(VariableSet variableSet, string thisVariableName)
        {
            foreach (Vertex vertex in variableSet.dependencyGraph.vertices())
            {
                if (vertex.name.Equals(thisVariableName))
                {
                    return vertex;
                }
            }

            return null;
        }


        private bool IsAspectCovered(FactVariable factVariable, string aspect)
        {
            if (factVariable.CoveredAspects.Contains(aspect))
            {
                return true;
            }

            return false;
        }


        private string GetLocation(FactItem item)
        {
            string location = "not-tuple";
            Concept conceptParent = dts.GetConceptByName(item.FactElement.Name.LocalName);

            if (conceptParent.SubstitutionGroup == Concept.EnumSubstitutionGroup.Tuple)
            {
                location = "tuple";
            }

            return location;
        }


        protected Resource GetResource(string resourceLabel, ExtendedLink extendedLinkParent)
        {
            foreach (Linkbase.Resource resource in extendedLinkParent.Resources)
            {
                if (resource.Label.Equals(resourceLabel))
                {
                    return resource;
                }
            }

            return null;
        }


        protected Locator GetLocator(Arc arc, string label)
        {
            foreach (Locator locator in arc.ExtendedLinkParent.Locators)
            {
                if (label.Equals(locator.Label))
                {
                    return locator;
                }
            }

            return null;
        }


        protected static List<List<FactItem>> ConvertToList(List<IEnumerable<FactItem>> cartesianProductMembersAllClone)
        {
            List<List<FactItem>> list = new List<List<FactItem>>();

            foreach (IEnumerable<FactItem> cartesianProductMemberAll in cartesianProductMembersAllClone)
            {
                list.Add(cartesianProductMemberAll.ToList());
            }

            return list;
        }


        private static void RegisterExtensionFunctions()
        {
            processor.RegisterExtensionFunction(new PrecisionXfi());

            // ===================================== Contexts and units ==================================================
            processor.RegisterExtensionFunction(new ContextXfi());
            processor.RegisterExtensionFunction(new UnitXfi());
            processor.RegisterExtensionFunction(new UnitNumeratorXfi());
            processor.RegisterExtensionFunction(new UnitDenominatorXfi());
            processor.RegisterExtensionFunction(new MeasureNameXfi());
            processor.RegisterExtensionFunction(new PeriodXfi());
            processor.RegisterExtensionFunction(new ContextPeriodXfi());
            processor.RegisterExtensionFunction(new IsStartEndPeriodXfi());
            processor.RegisterExtensionFunction(new IsForeverPeriodXfi());
            processor.RegisterExtensionFunction(new IsDurationPeriodXfi());
            processor.RegisterExtensionFunction(new IsInstantPeriodXfi());
            processor.RegisterExtensionFunction(new PeriodStartXfi());
            processor.RegisterExtensionFunction(new PeriodEndXfi());
            processor.RegisterExtensionFunction(new PeriodInstantXfi());
            processor.RegisterExtensionFunction(new EntityXfi());
            processor.RegisterExtensionFunction(new ContextEntityXfi());
            processor.RegisterExtensionFunction(new IdentifierXfi());
            processor.RegisterExtensionFunction(new ContextIdentifierXfi());
            processor.RegisterExtensionFunction(new EntityIdentifierXfi());
            processor.RegisterExtensionFunction(new IdentifierValueXfi());
            processor.RegisterExtensionFunction(new IdentifierSchemeXfi());
            processor.RegisterExtensionFunction(new SegmentXfi());
            processor.RegisterExtensionFunction(new EntitySegmentXfi());
            processor.RegisterExtensionFunction(new ContextSegmentXfi());
            processor.RegisterExtensionFunction(new ScenarioXfi());
            processor.RegisterExtensionFunction(new ContextScenarioXfi());

            // =============================== Facts and tuples ===========================================
            processor.RegisterExtensionFunction(new ItemsInInstanceXfi());
            processor.RegisterExtensionFunction(new TuplesInInstanceXfi());
            processor.RegisterExtensionFunction(new ItemsInTupleXfi());
            processor.RegisterExtensionFunction(new TuplesInTupleXfi());

            // ======================================== Predicates ==================================================
            processor.RegisterExtensionFunction(new IdenticalNodesXfi());
            processor.RegisterExtensionFunction(new SEqualXfi());
            processor.RegisterExtensionFunction(new UEqualXfi());

            // ======================================== Other Functions ==================================================
            processor.RegisterExtensionFunction(new FactExplicitDimensionValueXfi());
            processor.RegisterExtensionFunction(new FactDimensionSequal2Xfi());
        }


        protected void AddVertices(Graph g)
        {
            foreach (VariableSet variableSet in this.VariableSetArcs)
            {
                Vertex vertex = new Vertex(variableSet.Name);
                g.addVertex(vertex);
            }
        }


        protected void AddEdges(Graph g)
        {
            foreach (VariableSet variableSet in this.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("factVariable"))
                {
                    FactVariable factVariable = this.GetFactVariable(variableSet.Name, this);

                    foreach (Filter filter in factVariable.Filters)
                    {
                        foreach (Vertex vertex in g.vertices())
                        {
                            string sPattern = vertex.name + @"(\W+)";

                            if (Regex.IsMatch(filter.ResourceXElement.ToString(), sPattern, RegexOptions.IgnoreCase))
                            {
                                g.addEdge(variableSet.Name, vertex.name);
                            }
                        }
                    }
                }

                if (variableSet.Variable.Name.LocalName.Equals("generalVariable"))
                {
                    GeneralVariable generalVariable = GetGeneralVariable(this, variableSet);

                    foreach (Vertex vertex in g.vertices())
                    {
                        //string sPattern = vertex.name + @"(?![0-9]+)";
                        string sPattern = vertex.name + @"(\W+)";

                        if (Regex.IsMatch(generalVariable.Select, sPattern, RegexOptions.IgnoreCase))
                        {
                            g.addEdge(variableSet.Name, vertex.name);
                        }
                    }
                }
            }
        }
    }
}
